home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 1.iso
/
toolbox
/
src
/
exampleCode
/
opengl
/
xlib
/
dnbogl.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-11-11
|
38KB
|
982 lines
/*
* Copyright (c) 1993-94, Silicon Graphics, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the name of Silicon Graphics may not be used in any advertising or
* publicity relating to the software without the specific, prior written
* permission of Silicon Graphics.
*
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF THE
* POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* OpenGL(TM) is a trademark of Silicon Graphics, Inc.
*/
/*
* dnbogl.c : an openGL-Xlib dial-and-button box program imitating the
* behavior of the Dial & Button Confidence Test accessible
* via the System Manager toolchest. . This is the "after"
* version, ported from its "before" counterpart, located at
* ../../GLX/dials+buttons/dnbglx.c
*
* This program creates 2 openGL visuals placed side-by-side:
* 1. a colormap, singlebuffer'd openGL window is used to draw the
* buttons and text area (where animation is not necessary), and
* 2. a colormap, doublebuffer'd openGL window draws the dials
* (smooth animation *is* needed here).
*
* There are 2 functions prior to the infinite loop worth noting:
*
* 1. openwindow() sets up the X parent (including telling the WM what
* attributes and constraints we'd like to have), the two openGL
* children, specifies what events each window is interested in,
* loads the correct colormaps and then maps all three windows
* (they will not become visible however until the first expose
* events are generated/processed).
*
* 2. setupdevs() finds, opens, and creates a handle to the
* "dial+buttons" device structure. it then determines the given
* type and class of each device event we're going to be interested
* in, and then makes requests to the server to send us events that
* match the events and devices described by the event list *and*
* that come from our specific window.
*
* NOTE: the functionality of the GL routine SETVALUATOR(3G) (assign
* an initial value *and* range to a valuator) is not currently
* implemented in any X input extension from MIT. Hence, for
* the time being in this mixed-model imitation of the pure GL
* dial and buttons confidence test, the dial valuator's are
* NOT constrained in their range to only [0..1023]. This is
* the only known discrepancy between this program and the
* original. (There is a proposal to add this extension
* currently being debated but it is not finalized yet.)
*
* Following this, the get/process input infinite loop occupies the
* rest of the program's energies. the core of this is the "default"
* portion of the "switch (event.type)" statement which catches the
* dial and button events being generated. notice this is where the
* {DnB_motion_type, DnB_press_type, DnB_release_type} vars come into
* play: recall these were defined in setupdevs() with the 3 macros
* DeviceMotionNotify, DeviceButtonPress, and DeviceButtonRelease,
* respectively. XSelectExtensionEvent then was used to ask the
* server to send us any events generated by these devices in our
* window.
*
* X window functionality worth noting:
* dnbogl implements the funtionality in its window handling of creating
* a window the user is not allowed/able to resize utilizing the mwm
* MotifWindowHints structure. this kind of control of what the window
* can or can't be made to do can be useful at times.
* Also this program automatically places the window at a specific
* location on the console rather than letting the user interactively
* plant it upon startup.
* Both sets of functionality are defined in openwindow().
*
* IrisGL Functionality missing in OpenGL:
* the older dial and button box commands, dbtext() and setdblights(),
* are not supported in openGL so they are missing from this port.
*
* ratmandu -- ported to openGL, aug 93
*/
#include <stdio.h>
#include <stdlib.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glx.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <X11/extensions/XI.h>
#include <X11/extensions/XInput.h>
#define xsize 840 /* window's "unresize-able" dimensions */
#define ysize 480
#define NumButtons 32
#define NumDials 8
#define SWITCH_OFF 0 /* button states */
#define SWITCH_ON 1
#define top 0 /* the X parent window */
#define SBglwin 1 /* the "buttons" are drawn in a singlebuffered win */
#define DBglwin 2 /* and the "dials" are drawn in a doublebuffer one */
#define WINMAX 3
#define BLACK 0 /* "stand-ins" for GL colormap indices */
#define RED 1
#define BLUE 4
#define WHITE 7
typedef struct { /* struct for each button's bottom-left origin */
float xpos; /* (i.e. position), and whether its being pressed */
float ypos;
int state;
} DB_BUTTON;
DB_BUTTON but[] = { /* button positions & [initialized] state array */
11.0, 31.0, SWITCH_OFF,
16.0, 31.0, SWITCH_OFF,
21.0, 31.0, SWITCH_OFF,
26.0, 31.0, SWITCH_OFF,
6.0, 26.0, SWITCH_OFF,
11.0, 26.0, SWITCH_OFF,
16.0, 26.0, SWITCH_OFF,
21.0, 26.0, SWITCH_OFF,
26.0, 26.0, SWITCH_OFF,
31.0, 26.0, SWITCH_OFF,
6.0, 21.0, SWITCH_OFF,
11.0, 21.0, SWITCH_OFF,
16.0, 21.0, SWITCH_OFF,
21.0, 21.0, SWITCH_OFF,
26.0, 21.0, SWITCH_OFF,
31.0, 21.0, SWITCH_OFF,
6.0, 16.0, SWITCH_OFF,
11.0, 16.0, SWITCH_OFF,
16.0, 16.0, SWITCH_OFF,
21.0, 16.0, SWITCH_OFF,
26.0, 16.0, SWITCH_OFF,
31.0, 16.0, SWITCH_OFF,
6.0, 11.0, SWITCH_OFF,
11.0, 11.0, SWITCH_OFF,
16.0, 11.0, SWITCH_OFF,
21.0, 11.0, SWITCH_OFF,
26.0, 11.0, SWITCH_OFF,
31.0, 11.0, SWITCH_OFF,
11.0, 6.0, SWITCH_OFF,
16.0, 6.0, SWITCH_OFF,
21.0, 6.0, SWITCH_OFF,
26.0, 6.0, SWITCH_OFF,
};
typedef struct { /* struct for each dial's center point and */
float xcntr; /* current angle value (0 is at 12 o'clock)*/
float ycntr;
int angle;
} DB_DIAL;
DB_DIAL dia[] = { /* dial positions & initialized values array */
10.0, 8.0, 0,
20.0, 8.0, 0,
10.0, 15.0, 0,
20.0, 15.0, 0,
10.0, 22.0, 0,
20.0, 22.0, 0,
10.0, 29.0, 0,
20.0, 29.0, 0,
};
/* function declarations */
static void openwindow(char *);
static void initGL(void);
static void setupdevs(int *, int *, int *);
static void makeSBwin(void);
static void makeDBwin(void);
void Winset(int);
static void draw_dial(int);
static void draw_button(int, int);
static void makeRasterFont(Display **dpy);
static void printString(char *s);
static void clean_exit(void);
static Display *dpy; /* X server connection */
static Atom del_atom; /* WM_DELETE_WINDOW atom */
static Window glwins[WINMAX]; /* holds X parent & 2 GL window handles */
GLXContext glcontexts[WINMAX]; /* array for SB/DB window context ids */
/* dial and buttons device event handles */
int DnB_device_id; /* device handles for the */
int DnB_press_type; /* DnB id, and the press, */
int DnB_release_type; /* release, & motion */
int DnB_motion_type; /* events they'll generate */
short firstdialtouched = GL_FALSE; /* used so text for dial valuators */
/* doesn't get drawn until the dials */
/* actually are turned */
int myExpose; /* needs to be global for makeDBwin */
XEvent event; /* needs to be global for XIfEvent()*/
int dialaxis[NumDials]; /* tells which dial(s) is(are) currently active */
GLUquadricObj *qobj; /* for drawing the dials */
main(int argc, char *argv[])
{
int myConfigure, myMotion,
myButtPress, myButtRelease; /* track which events occur */
int buttP[NumButtons], buttR[NumButtons]; /* track which buttons are */
/* pressed or released */
int i;
int windowvisible; /* tells event handler whether or not window */
/* is iconified--when it is, we don't want */
/* to process any incoming events */
myExpose = myConfigure = myMotion = myButtPress = myButtRelease = GL_FALSE;
openwindow(argv[0]); /* configure/open up the window */
initGL();
setupdevs(buttP, buttR, dialaxis); /* set up the dial and button boxes */
makeRasterFont(&dpy); /* make font */
/*
* The event loop.
*/
while (1) { /* standard logic: get event(s), process event(s) */
int axis_data[6];
glFlush(); /* For proper DGL performance */
/* this "do while" loop does the `get events' half of the "get events,
* process events" action of the infinite while. this is to ensure
* the event queue is always drained before the events that have come
* in are processed.
*/
do {
XNextEvent(dpy, &event);
switch (event.type) {
/* "Expose" events are sort of like "REDRAW" in gl-speak in
* terms of when a window becomes visible, or a previously
* invisible part becomes visible.
*/
case Expose:
/* see if either or both of the GL windows needs redrawing.
* this way if only one of the two is generating the event,
* only it will get repainted.
*/
for (i=1; i<WINMAX; i++)
if (event.xexpose.window == glwins[i])
myExpose |= (1 << (i-1));
break;
/* "ConfigNotify" events are like "REDRAW" in terms of changes
* to a window's size or position. Since this prog locks
* the window's size, ConfigureNotify will occur only when
* the window gets moved.
*/
case ConfigureNotify:
myConfigure = GL_TRUE;
break;
/* "ButtonRelease" detects when LEFTMOUSE (Button1) is being
* pressed indicating its time to leave.
*/
case ButtonRelease:
if (event.xbutton.button == Button1)
clean_exit();
break;
/* "ClientMessage" is generated if the WM itself is being
* gunned down and sends an exit signal to any running prog.
*/
case ClientMessage:
if (event.xclient.data.l[0] == del_atom)
clean_exit();
break;
/* "MapNotify" is generated when the window is made visible.
*/
case MapNotify:
windowvisible = GL_TRUE;
break;
/* "UnmapNotify" is generated when the window is made "invisible"
* --like when the window is iconified. When the window is
* not visible (i.e. iconified), we don't want any events to
* be processed.
*/
case UnmapNotify:
windowvisible = GL_FALSE;
break;
/* since interest is on the dNb's, they become the default.
*/
default:
if (windowvisible) { /* only process events if win is */
/* visible (not if it's iconified) */
if (event.type == DnB_motion_type) { /* dial turned */
XDeviceMotionEvent *M=(XDeviceMotionEvent *)&event;
if (M->deviceid == DnB_device_id) {
i = M->first_axis; /* save out which dial */
dialaxis[i] = GL_TRUE;/*mark current "axis" */
dia[i].angle = M->axis_data[0];/* save angle */
myMotion = GL_TRUE; /* set motion flag */
} else
fprintf(stderr,"cable has been disconnected\n");
} else if (event.type == DnB_press_type) {
XDeviceButtonEvent *B=(XDeviceButtonEvent *)&event;
if (B->deviceid == DnB_device_id) {
buttP[B->button-1] = GL_TRUE;/*mrk cur butpres*/
myButtPress = GL_TRUE; /* set butpres flag */
} else
fprintf(stderr,"cable has been disconnected\n");
} else if (event.type == DnB_release_type) {
XDeviceButtonEvent *B=(XDeviceButtonEvent *)&event;
if (B->deviceid == DnB_device_id) {
buttR[B->button-1] = GL_TRUE;/*mrk cur butrel*/
myButtRelease = GL_TRUE; /* set butrel flag */
} else
fprintf(stderr,"cable has been disconnected\n");
}
}
break;
} /* end switch (event.type) */
} while (XPending(dpy)); /* end "do { } while".
* XPending() is like qtest()--it only
* tells you if there're any events
* presently in the queue. it does not
* disturb queue's contents in any way.
*/
/* On an "Expose" event, redraw the popped, deiconified, or exposed-by-
* another-window-being-pushed window.
*/
if (myExpose) {
if (myExpose & 2) {
Winset(DBglwin); /* draw doublebufr'd window*/
glViewport(0, 0, xsize-ysize-1, ysize-1);
makeDBwin();
}
if (myExpose & 1) {
Winset(SBglwin); /*draw singlebufr'd window*/
glViewport(0, 0, ysize-1, ysize-1);
makeSBwin();
if (myButtPress) { /* check to see if button is */
for (i=0; i<NumButtons; i++) { /* still being pressed */
if (buttP[i])
draw_button(i, SWITCH_ON);/* redraw pressed but */
}
}
}
myExpose = GL_FALSE; /* reset flag--queue now empty */
}
/* On a "ConfigureNotify" event, the "GL" window has been moved.
*/
if (myConfigure) {
Winset(DBglwin); /* draw the DB win's contents */
makeDBwin();
Winset(SBglwin); /* draw the SB win's contents */
makeSBwin();
if (myButtPress) { /* check to see if button is */
for (i=0; i<NumButtons; i++) { /* still being pressed */
if (buttP[i])
draw_button(i, SWITCH_ON); /* redraw pressed butt */
}
}
myConfigure = GL_FALSE; /* reset flag--queue now empty */
}
/* For a motion-type event (a dial turning), update the angle values
* of whichever dials have just be sent to the event queue.
*/
if (myMotion) {
Winset(DBglwin);
for (i=0; i<NumDials; i++) {
firstdialtouched = GL_TRUE; /* at this point a dial is */
draw_dial(i); /* being turned. */
dialaxis[i] = GL_FALSE;
}
glXSwapBuffers(dpy, glwins[DBglwin]);
myMotion = GL_FALSE;
}
/* For a "button press"-type event draw whichever buttons have been
* pressed since the last time the event queue was drained.
*/
if (myButtPress) {
Winset(SBglwin);
for (i=0; i<NumButtons; i++) {
if (buttP[i])
draw_button(i, SWITCH_ON); /* draw button being pressed */
}
}
/* For a "button release"-type event draw whichever buttons (in "off"
* position) have been released since last time the event q was drained.
*/
if (myButtRelease) {
Winset(SBglwin);
for (i=0; i<NumButtons; i++) {
if (buttR[i]) {
draw_button(i, SWITCH_OFF);
buttR[i] = GL_FALSE;
buttP[i] = GL_FALSE;
}
}
myButtPress = GL_FALSE;
myButtRelease = GL_FALSE;
}
}
}
#include <Xm/MwmUtil.h>
static int SBattributeList[] = { None };
static int DBattributeList[] = { GLX_DOUBLEBUFFER,
None };
/* WaitForNotify:
* used to make sure the MapWindow() calls inside openwindow() occur
* beFORE glXMakeCurrent() is invoked so as to avoid a race condition.
*/
static Bool WaitForNotify(Display *d, XEvent *e, char *arg) {
return (e->type == MapNotify) && (e->xmap.window == (Window)arg);
}
Colormap SBcmap, DBcmap; /* need both these handles in openwindow & initGL */
/* openwindow -
* establish connection to X server, get screen info, specify the
* attributes we want the WM to try to provide, and create the GL windows
*/
static void
openwindow(char *progname)
{
int screen_num; /* X screen number */
long xorig, yorig; /* window (upper-left) origin */
XSizeHints Winhints; /* used to fix window size */
Atom atomName; /* used to make parent */
MotifWmHints mwm; /* win non-resizeable */
XSetWindowAttributes SBswa, DBswa;
XVisualInfo *SBvi, *DBvi;
/* connect to the X server and get screen info */
if ((dpy = XOpenDisplay(NULL)) == NULL) {
fprintf(stderr, "%s: cannot connect to X server %s\n",
progname, XDisplayName(NULL));
exit(1);
}
screen_num = DefaultScreen(dpy);
/* define window (upper-left) origin coords */
xorig = (DisplayWidth(dpy, screen_num) - xsize) / 2;
yorig = (DisplayHeight(dpy, screen_num) - ysize) / 2;
/* create top level X window which will be the parent of the 2 GL windows */
glwins[top] = XCreateSimpleWindow(dpy, RootWindow(dpy, screen_num),
xorig, yorig, xsize, ysize, 0, 0, 0);
if (!(glwins[top])) {
fprintf(stderr,"%s: couldn't create \"parent\" X window\n",progname);
exit(1);
}
/* including this call to XSelectInput shud not be necessary, but there's
* a bug, so we have to resort to this hackaround.
*/
XSelectInput(dpy, glwins[top], StructureNotifyMask | ButtonPressMask |
ButtonReleaseMask | ExposureMask);
/* define string that will show up in the window title bar (and icon) */
XStoreName(dpy, glwins[top], "dial & button box confidence test");
/* specify the values for the Window Size Hints we want to enforce: this
* window can be moved but we don't want to allow any resizing to occur.
*/
Winhints.x = xorig; /* specify desired upper-left corner origin */
Winhints.y = yorig; /* of window so prog will place itself */
Winhints.width = xsize; /* specify desired x/y size of window */
Winhints.height = ysize;
Winhints.min_width = xsize; /* set min and max width and */
Winhints.max_width = xsize; /* height to be the same so */
Winhints.min_height = ysize; /* window cannot be resized */
Winhints.max_height = ysize;
Winhints.flags = USPosition|USSize|PMaxSize|PMinSize; /* set pert. flgs */
XSetNormalHints(dpy, glwins[top], &Winhints);
/* the following, culminating with XChangeProperty, enables us to remove
* the "[re]size" menu item from the default window menu, the "maximize"
* control button on the window frame, and the resize handle cursors
* that are normally available when the cursor moves into the proximity
* of any of the window's 4 corners.
*/
mwm.flags = MWM_HINTS_FUNCTIONS;
/* remove the decorations from the function list we don't want to have */
mwm.functions = MWM_FUNC_ALL | MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE;
mwm.decorations = 0;
mwm.input_mode = 0;
atomName = XInternAtom(dpy, _XA_MOTIF_WM_HINTS, False);
XChangeProperty(dpy, glwins[top], atomName, atomName, sizeof(long)*8,
PropModeReplace, (unsigned char*) &mwm, sizeof(mwm)/sizeof(long));
/* now define and create the single and doublebuffer rendering windows */
/* first, get the appropriate visuals */
SBvi = glXChooseVisual(dpy, screen_num, SBattributeList);
if (SBvi == NULL) {
fprintf(stderr,"cudn't create a singlebuffered visual (???)\n");
clean_exit();
}
DBvi = glXChooseVisual(dpy, screen_num, DBattributeList);
if (SBvi == NULL) {
fprintf(stderr,"cudn't create a doublebuffered visual (???)\n");
clean_exit();
}
/* now create the appropriate GLX contexts */
glcontexts[SBglwin] = glXCreateContext(dpy, SBvi, NULL, GL_FALSE);
glcontexts[DBglwin] = glXCreateContext(dpy, DBvi, NULL, GL_FALSE);
/* now create the two colormaps */
SBcmap = XCreateColormap(dpy, RootWindow(dpy, SBvi->screen),
SBvi->visual, AllocAll);
DBcmap = XCreateColormap(dpy, RootWindow(dpy, DBvi->screen),
DBvi->visual, AllocAll);
/* now create both windows */
SBswa.colormap = SBcmap;
SBswa.border_pixel = 0;
SBswa.event_mask = StructureNotifyMask | ButtonReleaseMask | ExposureMask |
Button1MotionMask | KeyPressMask; /* express interest in events */
glwins[SBglwin] = XCreateWindow(dpy, glwins[top],
0, 0, ysize, ysize,
0, SBvi->depth, InputOutput, SBvi->visual,
CWBorderPixel|CWColormap|CWEventMask, &SBswa);
DBswa.colormap = DBcmap;
DBswa.border_pixel = 0;
DBswa.event_mask = StructureNotifyMask | ButtonReleaseMask | ExposureMask |
Button1MotionMask | KeyPressMask; /* express interest in events */
glwins[DBglwin] = XCreateWindow(dpy, glwins[top],
ysize, 0, xsize, ysize,
0, DBvi->depth, InputOutput, DBvi->visual,
CWBorderPixel|CWColormap|CWEventMask, &DBswa);
/* now map the windows--they won't be made visible until we process the
* expose events up in main's infinite loop which these calls will
* generate. we use XIfEvent to prevent race conditions for the children
* SB and DB windows (which are the only ones we'll ever invoke in
* glXMakeCurrent())
*/
XMapWindow(dpy, glwins[DBglwin]);
XIfEvent(dpy, &event, WaitForNotify, (char*)glwins[DBglwin]);
XMapWindow(dpy, glwins[SBglwin]);
XIfEvent(dpy, &event, WaitForNotify, (char*)glwins[SBglwin]);
XMapWindow(dpy, glwins[top]); /* and finally map the X parent */
/* for starters, connect the context to the singlebuffer window */
if (!glXMakeCurrent(dpy, glwins[SBglwin], glcontexts[SBglwin]) == GL_TRUE) {
fprintf(stderr, "error w/glXMakeCurrent: cudn't set");
fprintf(stderr, " context to the singlebuffered GL window\n");
exit(-1);
}
/* ensure the GL colormap is installed for this app */
XSetWMColormapWindows(dpy, glwins[top], glwins, WINMAX);
/* express interest in WM being able to kill this app */
if ((del_atom = XInternAtom(dpy, "WM_DELETE_WINDOW", True)) != None)
XSetWMProtocols(dpy, glwins[top], &del_atom, 1);
glFlush();
}
static void
initGL(void)
{
XColor colorstruct;
colorstruct.pixel = BLACK; /* define colors we'll use */
colorstruct.red = 0;
colorstruct.green = 0;
colorstruct.blue = 0;
colorstruct.flags = DoRed | DoGreen | DoBlue;
XStoreColor(dpy, SBcmap, &colorstruct);
XStoreColor(dpy, DBcmap, &colorstruct);
colorstruct.pixel = RED;
colorstruct.red = 65535;
colorstruct.green = 0;
colorstruct.blue = 0;
colorstruct.flags = DoRed | DoGreen | DoBlue;
XStoreColor(dpy, SBcmap, &colorstruct);
XStoreColor(dpy, DBcmap, &colorstruct);
colorstruct.pixel = BLUE;
colorstruct.red = 0;
colorstruct.green = 0;
colorstruct.blue = 65535;
colorstruct.flags = DoRed | DoGreen | DoBlue;
XStoreColor(dpy, SBcmap, &colorstruct);
XStoreColor(dpy, DBcmap, &colorstruct);
colorstruct.pixel = WHITE;
colorstruct.red = 65535;
colorstruct.green = 65535;
colorstruct.blue = 65535;
colorstruct.flags = DoRed | DoGreen | DoBlue;
XStoreColor(dpy, SBcmap, &colorstruct);
XStoreColor(dpy, DBcmap, &colorstruct);
qobj = gluNewQuadric(); /* define basics for dial drawing */
gluQuadricDrawStyle(qobj, GLU_FILL);
Winset(DBglwin); /* define DB win's orthographic project */
glLoadIdentity();
gluOrtho2D(0.0, 30.0, 0.0, 40.0);
Winset(SBglwin); /* define SB win's orthographic project */
glLoadIdentity();
gluOrtho2D(0.0, 40.0, 0.0, 40.0);
glFlush();
}
/* setupdevs -
*
* establish a live connection to the dial+buttons device.
*
* leverages off the "X11 Input Extension Library Specification"
* document (you *shud* be able to locate the on-line public access
* directory which contains all the files to print hard-copy of this
* document under .../mit/doc/extensions/xinput). refer to
* /usr/include/X11/extensions/{XI.h, XInput.h} for structures accessed.
*/
static void
setupdevs(int buttP[NumButtons], int buttR[NumButtons], int dialaxis[NumDials])
{
int i, ndevices;
XDevice *DnB_device;
XDeviceInfoPtr lp, list;
int num_ext_event_classes;
XEventClass ListOfEventClass[3];
int DnB_press_class, DnB_release_class, DnB_motion_class;
/* get a ptr to the list of all currently defined input devices */
list = (XDeviceInfoPtr) XListInputDevices(dpy, &ndevices);
if (!list) {
fprintf(stderr,"XlistInputDevices failed to generate a devices list\n");
exit(1);
}
/* check out the /usr/people/4Dgifts/examples/devices/input/X/Xlist.c
* program (which gets compiled into "xlist"). running it will list
* all the currently available input devices on the machine xlist is
* run on. there is a LOT that should be studied in the "input" subtree.
*/
for (lp=list, i=0; i<ndevices; lp++, i++) {
if (lp->use==IsXExtensionDevice && strcmp(lp->name,"dial+buttons")==0) {
break; /* found the right one--now save the ptr (lp) to it */
}
}
if (i == ndevices) {
fprintf(stderr, "\"dial+buttons\" device was not found\n");
exit(1);
}
DnB_device = XOpenDevice(dpy, lp->id); /* open the DnB device */
if (!DnB_device) {
fprintf(stderr, "XOpenDevice failed for \"dial+buttons\" device\n");
exit(1);
}
DnB_device_id = DnB_device->device_id; /* save out the device id */
/* the following 3 macros determine the given event's type and class.
* each macro is passed the structure that describes the device from
* which input is desired.
*/
DeviceButtonPress(DnB_device, DnB_press_type, DnB_press_class);
DeviceButtonRelease(DnB_device, DnB_release_type, DnB_release_class);
DeviceMotionNotify(DnB_device, DnB_motion_type, DnB_motion_class);
ListOfEventClass[0] = DnB_press_class;
ListOfEventClass[1] = DnB_release_class;
ListOfEventClass[2] = DnB_motion_class;
num_ext_event_classes = 3;
/* XSelectExtensionEvent requests the server to send events that match
* the events and devices described by the event list and that come
* from the requested window.
* first, we'll put in our button press/release requests for the
* single-buffered (buttons and text) GL window.
*/
XSelectExtensionEvent(dpy, glwins[SBglwin],
ListOfEventClass, num_ext_event_classes);
/* now we'll request the Motion events for the double-buffered (dials)
* GL window.
* note that in each of these two invocations of XSelectExtensionEvent,
* we're selecting all three event classes in each window. this is
* because, no matter where the cursor is inside the window containing
* both the single and double -buffered GL windows, we want to process
* dial or button events, whether or not the mouse is physically
* present in either of these two respective windows.
*/
XSelectExtensionEvent(dpy, glwins[DBglwin],
ListOfEventClass, num_ext_event_classes);
/* initialize all the buttons and dial "markers" to indicate none have
* yet been pressed or moved.
*/
for (i=0; i<NumButtons; i++) {
buttP[i]=GL_FALSE;
buttR[i]=GL_FALSE;
}
for (i=0; i<NumDials; i++) {
dialaxis[i]=GL_FALSE;
}
}
char *typeToName[] = {
"Colormap single buffer",
"Colormap double buffer",
};
/* A little helper wrapper for glXMakeCurrent.
* passes the index to jointly access the windows and contexts array and
* checks the return value. This makes the call to begin GL drawing a
* little simpler. Building in such automatic error checking is always a
* "smooth move" (*not* like the cancerously-mutant human with the enlarged
* proboscis plastered all over the place urging people to be likewise
* smoothly cancerous and hardly cool).
*/
void
Winset(int type)
{
int rv;
XSync(dpy,GL_FALSE);
rv = glXMakeCurrent(dpy, glwins[type], glcontexts[type]);
if (rv == GL_FALSE) {
fprintf(stderr, "glXMakeCurrent failed for a %s-type window\n",
typeToName[type]);
exit(-1);
}
}
/* makeSBwin -- Draw the buttons and text in the singlebuffer'd GL window
*/
static void
makeSBwin(void)
{
int i;
glClearIndex(WHITE); /* clear the window */
glClear(GL_COLOR_BUFFER_BIT); /* background to white */
for (i=0; i<NumButtons; i++)
draw_button(i, SWITCH_OFF); /* draw the buttons */
glIndexi(BLACK);
glRasterPos2i(2, 1); /* "charstr()" replacement draws text */
printString("Use left mouse button to quit."); /* in lower left corner */
}
/* makeDBwin -- Draw the dials in the doublebuffer'd GL window
*/
static void
makeDBwin(void)
{
int i;
glClearIndex(WHITE); /* clear the window */
glClear(GL_COLOR_BUFFER_BIT); /* background to white */
for (i=0; i<NumDials; i++)
draw_dial(i); /* and draw the dials */
glXSwapBuffers(dpy, glwins[DBglwin]);
if (myExpose) { /* if an Expose is being */
glClearIndex(WHITE); /* processed, go ahead & */
glClear(GL_COLOR_BUFFER_BIT); /* draw the whole new */
for (i=0; i<NumDials; i++) /* back-buffer with what */
draw_dial(i); /* we just swaped into */
glXSwapBuffers(dpy, glwins[DBglwin]); /* the present front */
} /* (visible) buffer */
}
/* draw_dial -- Draw dial number dia[0...7]
*/
static void
draw_dial(int dialnum)
{
DB_DIAL *dptr = &dia[dialnum];
float xcenter, ycenter;
int angle;
char str[80];
xcenter = dptr->xcntr;
ycenter = dptr->ycntr;
angle = dptr->angle;
if ((firstdialtouched) && /* when we know a dial has */
(dialaxis[dialnum])) { /* actually been turned, update */
Winset(SBglwin); /* the text area that resides in */
glIndexi(WHITE); /* the singlebuffer'd GL window */
glRecti(2, 2, 24, 5);
sprintf(str,"Dial %d intensity is %d",dialnum,angle);
glIndexi(BLUE);
glRasterPos2i(2, 3);
printString(str);
glFlush();
}
Winset(DBglwin); /* now switch cur gfx context to */
angle = (dptr->angle * 3600) >> 10; /* doublebuffer'd window & update*/
glIndexi(WHITE); /* dial currently being turned: */
glPushMatrix(); /* FIRST: "white-out" the entire*/
glTranslatef(xcenter, ycenter, 0.0);
gluDisk(qobj, 0.0, 2.5, 32, 1);
glPopMatrix();
glIndexi(BLACK);
glPushMatrix();
glTranslatef(xcenter, ycenter, 0.0);
gluDisk(qobj, 2.4, 2.5, 32, 1);
glPopMatrix();
glIndexi(RED);
glPushMatrix();
glTranslatef(xcenter, ycenter, 0.0);
gluPartialDisk(qobj, 0.0, 2.5, 32, 1, 0.0, angle*0.1);
glPopMatrix();
glFlush();
}
#define BSIZE 3.0
/* draw_button -- Draw button number buttno in its on or off state
*/
static void
draw_button(int buttno, int state)
{
DB_BUTTON *bptr = &but[buttno];
unsigned long button_led = 0;
float xpos, ypos;
char str[80];
xpos = bptr->xpos;
ypos = bptr->ypos;
if (state == SWITCH_ON) {
glIndexi(WHITE); /* white out the text area to */
glRecti(2, 2, 24, 5); /* prepare for new button press */
sprintf(str, "Button %d is being pressed", buttno);
glIndexi(BLUE);
glRasterPos2i(2, 3); /* update text string in bottom- */
printString(str); /* -left corner of the SB window */
/* now draw button being pressed in RED--have to resort to a hack here:
* in older IrisGL, cmds like rect rendered "bloated" polygons where an
* outline would be made around the area to be filled and then the
* outline would be drawn over as well. In the current point-sampled
* approach, two adjacent sides do NOT get drawn over, thus the need to
* do a WHITE glRecti first with each edge being expanded away from the
* center by 1 pixel. (to observe the difference, comment out the next
* two lines and see the differing results.)
*/
glIndexi(WHITE);
glRecti(xpos-1, ypos-1, xpos+BSIZE+1, ypos+BSIZE+1);
glIndexi(RED);
glRecti(xpos, ypos, xpos+BSIZE, ypos+BSIZE);
} else {
glIndexi(WHITE);
glRecti(2, 2, 24, 5); /* white out area where text was */
glRecti(xpos, ypos, xpos+BSIZE, ypos+BSIZE);
glPolygonMode(GL_FRONT, GL_LINE); /* now draw in "outline" mode to */
glIndexi(BLACK); /* draw an unpressed button, and */
glRecti(xpos, ypos, xpos+BSIZE, ypos+BSIZE); /* when we're finished */
glPolygonMode(GL_FRONT, GL_FILL); /* return to "fill"/default mode */
}
}
/* makeRasterFont() and printString() are lifted out of font.c (lives in
* this same directory) as a replacement to the IrisGL charstr() function.
*/
GLuint base;
static void makeRasterFont(Display **dpy)
{
XFontStruct *fontInfo;
Font id;
unsigned int first, last;
fontInfo = XLoadQueryFont(*dpy,
"-sgi-screen-bold-r-normal--15-150-72-72-m-90-iso8859-1");
if (fontInfo == NULL) {
printf("no font found\n");
exit(0);
}
id = fontInfo->fid;
first = fontInfo->min_char_or_byte2;
last = fontInfo->max_char_or_byte2;
base = glGenLists(last+1);
if (base == 0) {
printf("out of display lists\n");
exit(0);
}
glXUseXFont(id, first, last-first+1, base+first);
}
static void printString(char *s)
{
glPushAttrib(GL_LIST_BIT);
glListBase(base);
glCallLists(strlen(s), GL_UNSIGNED_BYTE, (unsigned char *)s);
glPopAttrib();
}
/* clean_exit -- Clean up before exiting
*/
static void
clean_exit(void)
{
XCloseDisplay(dpy);
exit(0);
}